package com.sharp.core.voxel;

import java.util.ArrayList;
import java.util.List;

import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;

public class Polygonizator {
	
	public VoxelRenderList polygonize(IsoSurface map, float cellSize, float isoLevel) {
		
		VoxelRenderList list = new VoxelRenderList();
		
		for ( float z = 0; z < map.getFieldDepth(); z += cellSize ) {
			for ( float y = 0; y < map.getFieldHeight(); y += cellSize ) {					
				for ( float x = 0; x < map.getFieldWidth(); x += cellSize ) {
					GridCell cell = map.getCell(x, y, z, cellSize);
					if ( cell == null || cell.isInLevel(isoLevel) == false )
						continue;
					ColorRGBA color = cell.getColor();
					List<Vector3f> vertices = polygonize(cell, isoLevel);
					for ( int i = 0; i < vertices.size(); ++i )
						list.addColor(color);
					list.addVertices( vertices );
				}
			}
		}
		return list;
	}
	
	private List<Vector3f> polygonize(GridCell cell, float isoLevel) {
		int cubeIndex = cell.getCubeIndex(isoLevel);
		if ( PolygonizatorConsts.edgeTable[cubeIndex] == 0 )
			return null;
		Vector3f[] vertList = getVertexList(cell, isoLevel, cubeIndex);
		
		List<Vector3f> list = new ArrayList<Vector3f>();
		for ( int i = 0; PolygonizatorConsts.triTable[cubeIndex][i] != -1; i += 3 ) {
			Vector3f[] tri = new Vector3f[3];
			tri[0] = vertList[ PolygonizatorConsts.triTable[cubeIndex][i  ] ];
			tri[1] = vertList[ PolygonizatorConsts.triTable[cubeIndex][i+1] ];
			tri[2] = vertList[ PolygonizatorConsts.triTable[cubeIndex][i+2] ];
			list.add(tri[2]);
			list.add(tri[1]);
			list.add(tri[0]);
		}
		
		return list;
	}
	
	private Vector3f interpolate(float isolevel, GridCell cell, int i1, int i2) {
		Vector3f p1 = cell.getVertex(i1);
		Vector3f p2 = cell.getVertex(i2);
		float v1 = cell.getValue(i1);
		float v2 = cell.getValue(i2);
		float delta = (isolevel - v1) / (v2-v1);
		
		Vector3f ret = new Vector3f(
						p1.x + delta*(p2.x - p1.x),
						p1.y + delta*(p2.y - p1.y),
						p1.z + delta*(p2.z - p1.z));
		return ret;
	}
	
	private Vector3f[] getVertexList(GridCell cell, float isoLevel, int cubeIndex) {
		Vector3f[] vertList = new Vector3f[12]; 
		if ((PolygonizatorConsts.edgeTable[cubeIndex] & 1) != 0)
			vertList[0] = interpolate(isoLevel, cell, 0, 1);
		
		if ((PolygonizatorConsts.edgeTable[cubeIndex] & 2) != 0)
			vertList[1] = interpolate(isoLevel, cell, 1, 2);
		
		if ((PolygonizatorConsts.edgeTable[cubeIndex] & 4) != 0)
			vertList[2] = interpolate(isoLevel, cell, 2, 3);
		
		if ((PolygonizatorConsts.edgeTable[cubeIndex] & 8) != 0)
			vertList[3] = interpolate(isoLevel, cell, 3, 0);
		
		if ((PolygonizatorConsts.edgeTable[cubeIndex] & 16) != 0)
			vertList[4] = interpolate(isoLevel, cell, 4, 5);
		
		if ((PolygonizatorConsts.edgeTable[cubeIndex] & 32) != 0)
			vertList[5] = interpolate(isoLevel, cell, 5, 6);
		
		if ((PolygonizatorConsts.edgeTable[cubeIndex] & 64) != 0)
			vertList[6] = interpolate(isoLevel, cell, 6, 7);
		
		if ((PolygonizatorConsts.edgeTable[cubeIndex] & 128) != 0)
			vertList[7] = interpolate(isoLevel, cell, 7, 4);
		
		if ((PolygonizatorConsts.edgeTable[cubeIndex] & 256) != 0)
			vertList[8] = interpolate(isoLevel, cell, 0, 4);
		
		if ((PolygonizatorConsts.edgeTable[cubeIndex] & 512) != 0)
			vertList[9] = interpolate(isoLevel, cell, 1, 5);
		
		if ((PolygonizatorConsts.edgeTable[cubeIndex] & 1024) != 0)
			vertList[10] = interpolate(isoLevel, cell, 2, 6);
		
		if ((PolygonizatorConsts.edgeTable[cubeIndex] & 2048) != 0)
			vertList[11] = interpolate(isoLevel, cell, 3, 7);
		return vertList;
	}
	
}
